﻿using AutoMapper;
using Mes.Application.Common.Resource;
using Mes.Application.Common;
using Mes.Application.DTO.Inputs.WorkOrders;
using Mes.Infrastructure.Data.BoundedContext.Repositories.WorkOrders;
using Mes.Infrastructure.Data.BoundedContext.Repositories.MasterData;
using Mes.Domain.BoundedContext.Entities.WorkOrders;
using Microsoft.EntityFrameworkCore;
using Mes.Domain.BoundedContext;
using Mes.Application.DTO.Results.WorkOrders;
using Mes.Infrastructure.Data.BoundedContext.Configurations;
using Microsoft.Extensions.Options;

namespace Mes.Application.Services.WorkOrders
{
    public class WoFlowService : IWoFlowService
    {
        private readonly IMapper _mapper;
        private readonly IJobRepository _jobRepository;
        private readonly IJobExecRepository _jobExecRepository;
        private readonly IMaterialRepository _materialRepository;
        private readonly IItemProdRepository _itemProdRepository;
        private readonly IWoRepository _woRepository;
        private readonly IJobStateRepository _jobStateRepository;
        private readonly IProductionUnitRepository _productionUnitRepository;
        private readonly IJobBOMRepository _jobBOMRepository;
        private readonly IJobStepDataRepository _jobStepDataRepository;
        private readonly BuiltInTypesConfiguration _builtInTypes;
        private readonly IItemConsRepository _itemConsRepository;
        private readonly IJobStepGrpRepository _jobStepGrpRepository;
        private readonly IJobStepRepository _jobStepRepository;

        public WoFlowService(IMapper mapper, IJobRepository jobRepository, IJobExecRepository jobExecRepository, IMaterialRepository materialRepository, IItemProdRepository itemProdRepository, IWoRepository woRepository, IJobStateRepository jobStateRepository, IProductionUnitRepository productionUnitRepository, IJobBOMRepository jobBOMRepository, IJobStepDataRepository jobStepDataRepository, IOptions<BuiltInTypesConfiguration> options, IItemConsRepository itemConsRepository, IJobStepGrpRepository jobStepGrpRepository, IJobStepRepository jobStepRepository)
        {
            if (mapper == null || jobRepository == null || jobExecRepository == null || itemProdRepository == null || woRepository == null || jobStateRepository == null || jobBOMRepository == null || jobStepDataRepository == null || itemConsRepository == null || jobStepGrpRepository == null || jobStepRepository == null)
            {
                throw new ArgumentNullException();
            }
            _mapper = mapper;
            _jobRepository = jobRepository;
            _jobExecRepository = jobExecRepository;
            _materialRepository = materialRepository;
            _itemProdRepository = itemProdRepository;
            _woRepository = woRepository;
            _jobStateRepository = jobStateRepository;
            _productionUnitRepository = productionUnitRepository;
            _jobBOMRepository = jobBOMRepository;
            _jobStepDataRepository = jobStepDataRepository;
            _builtInTypes = options.Value;
            _itemConsRepository = itemConsRepository;
            _jobStepGrpRepository = jobStepGrpRepository;
            _jobStepRepository = jobStepRepository;
        }

        /// <summary>
        /// 增加生产
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task AddItemProdAsync(AddingItemProdInput input)
        {
            //判断作业是否存在
            var job = await _jobRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.JobNotFound);
            }
            // 当前job必须是运行状态
            if (job.State_Cd != 3)
            {
                throw new CustomException(Messages.JobNotRunning);
            }
            // 当前job必须运行在指定的entity上
            var jobExec = await _jobExecRepository.FindAsync(x => x.Ent_Id == input.Ent_Id && x.Cur_Wo_Id == input.Wo_Id && x.Cur_Oper_Id == input.Oper_Id);
            if (jobExec == null)
            {
                throw new CustomException(Messages.JobNotRunningOnEntity);
            }
            //判断物料是否存在
            if (!await _materialRepository.ExistsAsync(x => x.Id == input.Item_Id))
            {
                throw new CustomException(Messages.MaterialNotFound);
            }
            if (input.Item_Id != job.Item_Id)
            {
                throw new CustomException(Messages.ItemIdOfJobIsDifferent);
            }
            var itemProd = await _itemProdRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Ent_Id == input.Ent_Id && x.Item_Id == input.Item_Id && x.Good_Prod == input.Good_Prod);
            if (itemProd == null)  // 当item_prod无记录时，添加一条记录
            {
                var newItemProd = _mapper.Map<ItemProd>(input);
                await _itemProdRepository.AddAsync(newItemProd);
            }
            else // 当item_prod有记录时，item_prod.Qty_Prod=item_prod.Qty_Prod+input.Qty_Prod
            {
                itemProd.Qty_Prod = itemProd.Qty_Prod + input.Qty_Prod;
                itemProd.Last_Edit_At = DateTime.Now;
            }
            var status = await _itemProdRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
            //更新作业的良品量和次品量
            // job.Qty_Prod=Sum(itemProd.Qty_Prod) where Good_Prod=true;
            // job.Qty_Rejected=Sum(itemProd.Qty_Prod) where Good_Prod=false;
            var allProd = await _itemProdRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id).ToListAsync();
            var totalQtyGood = allProd.Where(x => x.Good_Prod == true).Sum(x => x.Qty_Prod);
            var totalQtyRejected = allProd.Where(x => x.Good_Prod == false).Sum(x => x.Qty_Prod);
            job.Qty_Prod = totalQtyGood;
            job.Qty_Rejected = totalQtyRejected;

            var status1 = await _jobRepository.SaveAsync();
            if (status1 <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 减少生产
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task ReduceItemProdAsync(ReducingItemProdInput input)
        {
            //判断生产单元是否存在
            if (!await _productionUnitRepository.ExistsAsync(x => x.Id == input.Ent_Id))
            {
                throw new CustomException(Messages.ProductionUnitNotFound);
            }
            //判断目标存储位置的生产单元是否存在
            if (!await _productionUnitRepository.ExistsAsync(x => x.Id == input.To_Ent_Id))
            {
                throw new CustomException(Messages.ProductionUnitNotFound);
            }
            //判断物料是否存在
            var material = await _materialRepository.FindAsync(x => x.Id == input.Item_Id);
            if (material == null)
            {
                throw new CustomException(Messages.MaterialNotFound);
            }
            //判断作业是否存在
            var job = await _jobRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.JobNotFound);
            }
            //验证作业的物料ID和入参物料ID是否一直
            if (job.Item_Id != input.Item_Id)
            {
                throw new CustomException(Messages.ItemIdOfJobIsDifferent);
            }
            //如果减少的是良品量，则需要判断需要减少的良品量不能大于作业的良品量
            if (input.Good_Prod == true && input.Qty_Prod > job.Qty_Prod)
            {
                throw new CustomException(Messages.NotEnoughReduce);
            }
            //如果减少的是次品量，则需要判断需要减少的次品量不能大于作业的次品量
            if (input.Good_Prod == false && input.Qty_Prod > job.Qty_Rejected)
            {
                throw new CustomException(Messages.NotEnoughReduce);
            }
            //查询产量记录
            var itemProd = await _itemProdRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Item_Id == input.Item_Id && x.Ent_Id == input.Ent_Id && x.Good_Prod == input.Good_Prod);
            if (itemProd == null)
            {
                throw new CustomException(Messages.ItemProdNotExist);
            }
            itemProd.Qty_Prod = itemProd.Qty_Prod - input.Qty_Prod;
            if (input.Good_Prod == true)//减少的是良品量，则作业的良品量相应减少
            {
                job.Qty_Prod = job.Qty_Prod - input.Qty_Prod;
            }
            else//减少的是次品量，则作业的次品量相应减少
            {
                job.Qty_Rejected = job.Qty_Rejected - input.Qty_Prod;
            }

            await _jobRepository.SaveAsync();
            if (itemProd.Qty_Prod == 0)
            {
                await _itemProdRepository.DeleteAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Item_Id == input.Item_Id && x.Good_Prod == true);
                await _itemProdRepository.SaveAsync();
            }
        }

        /// <summary>
        /// 查询作业队列
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<ItemsResult<EntJobsResult>> GetEntJobsAsync(GettingEntJobsInput input)
        {
            var ent = await _productionUnitRepository.FindAsync(x => x.Id == input.Ent_Id);
            if (ent == null)
            {
                throw new CustomException(Messages.EntityNotFound);
            }
            // 查询所选实体中的所有job
            var jobs = await _jobRepository.Query().Where(x => x.Init_Sched_Ent_Id == input.Ent_Id).OrderBy(x => x.Wo_Id).OrderBy(x => x.Display_Seq).ToListAsync();
            var itemIds = jobs.Select(x => x.Item_Id).Distinct().ToList();
            var materials = await _materialRepository.Query().Where(x => itemIds.Contains(x.Id)).ToListAsync();
            var woIds = jobs.Select(x => x.Wo_Id).Distinct().ToList();
            var wos = await _woRepository.Query().Where(x => woIds.Contains(x.Wo_Id)).ToListAsync();
            var stateCds = jobs.Select(x => x.State_Cd).Distinct().ToList();
            var states = await _jobStateRepository.Query().Where(x => stateCds.Contains(x.State_Cd)).ToListAsync();
            var data = _mapper.Map<List<EntJobsResult>>(jobs);
            foreach (var item in data)
            {
                item.Wo_Desc = wos.Where(x => x.Wo_Id == item.Wo_Id).FirstOrDefault()?.Wo_Desc ?? string.Empty;
                item.Item_Name = materials.Where(x => x.Id == item.Item_Id).FirstOrDefault()?.Name ?? string.Empty;
                item.State_Desc = states.Where(x => x.State_Cd == item.State_Cd).FirstOrDefault()?.State_Desc ?? string.Empty;
                item.Init_Sched_Ent_Name = ent?.ProductionUnitName ?? string.Empty;
            }
            return new ItemsResult<EntJobsResult>() { Items = data };
        }

        /// <summary>
        /// 查询正在运行的job的产量列表、物料清单、工步列表数据集
        /// </summary>
        /// <param name="wo_Id">工单编号</param>
        /// <param name="oper_Id">操作编号</param>
        /// <returns></returns>
        public async Task<RunningJobProdBOMStepResult> GetRunningJobProdsBOMsStepsAsync(string wo_Id, string oper_Id)
        {
            var job = await _jobRepository.FindAsync(x => x.Wo_Id == wo_Id && x.Oper_Id == oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.JobNotFound);
            }
            if (job.State_Cd != 3)
            {
                throw new CustomException(Messages.JobNotRunning);
            }

            //产量
            var itemProds = await _itemProdRepository.Query().Where(x => x.Wo_Id == wo_Id && x.Oper_Id == oper_Id).ToListAsync();
            var prodData = _mapper.Map<List<ItemProdResult>>(itemProds);
            var itemIds = prodData.Select(x => x.Item_Id).Distinct().ToList();
            var materials = await _materialRepository.Query().Where(x => itemIds.Contains(x.Id)).ToListAsync();
            var toEntIds = prodData.Select(x => x.To_Ent_Id).Distinct().ToList();
            var toEnts = await _productionUnitRepository.Query().Where(x => toEntIds.Contains(x.Id)).ToListAsync();
            foreach (var item in prodData)
            {
                item.Item_Name = materials.Where(x => x.Id == item.Item_Id).FirstOrDefault()?.Name ?? string.Empty;
                item.To_Ent_Name = toEnts.Where(x => x.Id == item.To_Ent_Id).FirstOrDefault()?.ProductionUnitName ?? string.Empty;
            }

            ///物料清单
            var jobBoms = await _jobBOMRepository.Query().Where(x => x.Wo_Id == wo_Id && x.Oper_Id == oper_Id).ToListAsync();
            var bomData = _mapper.Map<List<RunningJobBomResult>>(jobBoms);
            var bomItemIds = bomData.Select(x => x.Item_Id).Distinct().ToList();
            var bomMaterials = await _materialRepository.Query().Where(x => bomItemIds.Contains(x.Id)).ToListAsync();
            var itemCons = await _itemConsRepository.Query().Where(x => x.Wo_Id == wo_Id && x.Oper_Id == oper_Id).ToListAsync();
            //成品物料需要移除
            var finishedMaterialID = 0;
            foreach (var item in bomData)
            {
                var currentMaterial = bomMaterials.Where(x => x.Id == item.Item_Id).FirstOrDefault();
                if (currentMaterial?.MaterialClassTypeName == _builtInTypes.BuiltInMaterialClassTypes[0])
                {
                    finishedMaterialID = currentMaterial.Id;
                }
                item.Item_Name = currentMaterial?.Name ?? string.Empty;
                if (job.Qty_Prod <= job.Qty_At_Start)
                {
                    item.Reqd_Qty_Prod = job.Qty_At_Start * item.Qty_Per_Parent_Item;
                }
                else
                {
                    item.Reqd_Qty_Prod = job.Qty_Prod * item.Qty_Per_Parent_Item;
                }
                item.Qty_Cons = itemCons.Where(x => x.Item_Id == item.Item_Id)?.Sum(x => x.Qty_Cons) ?? 0;
                item.ProductionQuantity = job.Qty_At_Start;
            }
            bomData.RemoveAll(x => x.Item_Id == finishedMaterialID);

            //工步组
            var stepGrps = await _jobStepGrpRepository.Query().Where(x => x.Wo_Id == wo_Id && x.Oper_Id == oper_Id).OrderBy(x => x.Step_Grp_Seq).ToListAsync();
            var steps = await _jobStepRepository.Query().Where(x => x.Wo_Id == wo_Id && x.Oper_Id == oper_Id).ToListAsync();
            var stepDatas = await _jobStepDataRepository.Query().Where(x => x.Wo_Id == wo_Id && x.Oper_Id == oper_Id).ToListAsync();
            var stepGrpData = _mapper.Map<List<RunningJobStepGrpResult>>(stepGrps);
            var jobstates = await _jobStateRepository.Query().ToListAsync();
            bool ExistReadyStep = false;
            bool ExistRunningStep = false;
            foreach (var grp in stepGrpData)
            {
                var currentSteps = steps.Where(x => x.Step_Grp_Id == grp.Step_Grp_Id).OrderBy(x => x.Step_Seq).ToList();
                var currenStepResult = new List<RunningJobStepResult>();
                for (int i = 0; i < currentSteps.Count; i++)
                {
                    var currentStepDatas = stepDatas.Where(x => x.Step_Grp_Id == grp.Step_Grp_Id && x.Step_No == currentSteps[i].Step_No).OrderBy(x => x.Row_Id).ToList();
                    //第一个未修改为状态的的工步，而且此工步之前的工步状态全部是Complete或者SUPERSEDED时，此工步的状态是Ready,其他情况状态为New
                    if (currentStepDatas.Count <= 0)
                    {
                        if (ExistReadyStep == false&& ExistRunningStep==false)
                        {
                            currenStepResult.Add(new RunningJobStepResult { Step_No = currentSteps[i].Step_No, Step_Desc = currentSteps[i].Step_Desc, State_Cd = 2, State_Desc = jobstates.FirstOrDefault(x => x.State_Cd == 2)?.State_Desc ?? string.Empty });
                            ExistReadyStep = true;
                        }
                        else
                        {
                            currenStepResult.Add(new RunningJobStepResult { Step_No = currentSteps[i].Step_No, Step_Desc = currentSteps[i].Step_Desc, State_Cd = 1, State_Desc = jobstates.FirstOrDefault(x => x.State_Cd == 1)?.State_Desc ?? string.Empty });
                        }
                    }
                  
                    //如果状态修改过，则从job_step_data表中会存相应的状态和操作时间数据
                    if (currentStepDatas.Count > 0)
                    {
                        for (int j = 0; j < currentStepDatas.Count; j++)
                        {
                            if (currentStepDatas[j].State_Cd == 3)
                            {
                                ExistRunningStep = true;
                            }
                            currenStepResult.Add(
                                new RunningJobStepResult
                                {
                                    Step_No = currentSteps[i].Step_No,
                                    State_Cd = currentStepDatas[j].State_Cd,
                                    Act_Finish_Time_Local = currentStepDatas[j].Act_Finish_Time_Local,
                                    Act_Start_Time_Local = currentStepDatas[j].Act_Start_Time_Local,
                                    Step_Desc = currentSteps[i].Step_Desc,
                                    State_Desc = jobstates.FirstOrDefault(x => x.State_Cd == currentStepDatas[j].State_Cd)?.State_Desc ?? string.Empty
                                });
                            //如果最后一步数据状态是SUPERSEDED，则向下面再添加一条Ready的数据
                            if (j == currentStepDatas.Count - 1 && currentStepDatas[j].State_Cd == 9)
                            {
                                currenStepResult.Add(
                               new RunningJobStepResult
                               {
                                   Step_No = currentSteps[i].Step_No,
                                   State_Cd = 2,
                                   Step_Desc = currentSteps[i].Step_Desc,
                                   State_Desc = jobstates.FirstOrDefault(x => x.State_Cd == 2)?.State_Desc ?? string.Empty
                               });
                                ExistReadyStep = true;
                            }
                        }
                    }
                    grp.RunningJobStepResults = currenStepResult;
                }
            }

            return new RunningJobProdBOMStepResult { ItemProdResults = prodData, RunningJobBomResults = bomData, RunningJobStepGrpResults = stepGrpData };
        }

        /// <summary>
        /// 添加消耗量
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task AddItemConsAsync(AddingItemConsInput input)
        {
            //判断作业是否存在
            var job = await _jobRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.JobNotFound);
            }
            //当前job必须是运行状态
            if (job.State_Cd != 3)
            {
                throw new CustomException(Messages.JobNotRunning);
            }
            //当前job必须运行在指定的entity上
            var jobExec = await _jobExecRepository.FindAsync(x => x.Ent_Id == input.Ent_Id && x.Cur_Wo_Id == input.Wo_Id && x.Cur_Oper_Id == input.Oper_Id);
            if (jobExec == null)
            {
                throw new CustomException(Messages.JobNotRunningOnEntity);
            }
            //判断物料是否存在
            var material = await _materialRepository.FindAsync(x => x.Id == input.Item_Id);
            if (material == null)
            {
                throw new CustomException(Messages.MaterialNotFound);
            }
            //判断物料是否是消耗品
            if (material.MaterialClassTypeName != _builtInTypes.BuiltInMaterialClassTypes[1])
            {
                throw new CustomException(Messages.MaterialClassTypeNotConsumables);
            }
            //判断生产单元是否存在
            if (!await _productionUnitRepository.ExistsAsync(x => x.Id == input.Ent_Id))
            {
                throw new CustomException(Messages.ProductionUnitNotFound);
            }
            //判断目标存储位置（生产单元）是否存在
            if (input.From_Ent_Id != null)
            {
                if (!await _productionUnitRepository.ExistsAsync(x => x.Id == input.From_Ent_Id))
                {
                    throw new CustomException(Messages.ProductionUnitNotFound);
                }
            }
            //判断jobBOM是否存在
            var jobBom = await _jobBOMRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Item_Id == input.Item_Id);
            if (jobBom == null)
            {
                throw new CustomException(Messages.JobBomNotFound);
            }
            var itemCons = await _itemConsRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Item_Id == input.Item_Id && x.Ent_Id == input.Ent_Id && x.From_Ent_Id == input.From_Ent_Id);
            if (itemCons == null)//如果消耗记录不存在，则添加一条记录
            {
                var newItemCons = _mapper.Map<ItemCons>(input);
                await _itemConsRepository.AddAsync(newItemCons);
            }
            else //如果消耗记录存在，则添加消耗量
            {
                itemCons.Qty_Cons = itemCons.Qty_Cons + input.Qty_Cons;
                itemCons.Last_Edit_At = DateTime.Now;
            }
            var status = await _itemConsRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 启动工步（工步状态由Ready->Running）
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task StartJobStepAsync(StartingJobStepInput input)
        {
            //当前job必须运行在指定的entity上
            var jobExec = await _jobExecRepository.FindAsync(x => x.Ent_Id == input.Ent_Id && x.Cur_Wo_Id == input.Wo_Id && x.Cur_Oper_Id == input.Oper_Id);
            if (jobExec == null)
            {
                throw new CustomException(Messages.JobNotRunningOnEntity);
            }
            //判断工步是否存在
            var step = await _jobStepRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Step_Grp_Id == input.Step_Grp_Id && x.Step_No == input.Step_No);
            if (step == null)
            {
                throw new CustomException(Messages.JobStepNotFound);
            }
            var stepData = await _jobStepDataRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Step_Grp_Id == input.Step_Grp_Id && x.Step_No == input.Step_No && x.State_Cd != 9).OrderByDescending(x => x.Row_Id).FirstOrDefaultAsync();

            if (stepData != null)
            {
                if (stepData.Act_Start_Time_Local != null)
                {
                    if (stepData.State_Cd != 4 && stepData.State_Cd != 8 && stepData.State_Cd != 9) //当jobStepData存在数据，且数据的Act_Start_Time_Local不为空，而且状态不等于4、8、9则代表工步已经开始运行，不能将工步启动，报错
                    {
                        throw new CustomException(Messages.JobStepAlreadyRunning);
                    }
                    //当jobStepData存在数据，且数据的Act_Start_Time_Local不为空，而且状态等于4或者8或者9，开启工步，修改jobStepData相关的启动数据
                    stepData.State_Cd = 3;
                    stepData.Act_Start_Time_Local = DateTime.Now;
                    stepData.Act_Start_Time_Utc = DateTime.UtcNow;
                    stepData.Last_Edit_At = DateTime.Now;
                }
            }
            else //当jobStepData不存在数据，向jobStepData添加一条启动数据
            {
                var newStepData = new JobStepData(input.Wo_Id, input.Oper_Id, input.Step_No, input.Step_Grp_Id, 3, DateTime.UtcNow, DateTime.Now, DateTime.Now);
                await _jobStepDataRepository.AddAsync(newStepData);
            }
            //将JobExec当前工步修改为输入的工步
            jobExec.Cur_Step_Grp_Id = input.Step_Grp_Id;
            jobExec.Cur_Step_No = input.Step_No;
            jobExec.Cur_Step_Start = DateTime.UtcNow;
            jobExec.Last_Edit_At = DateTime.Now;
            var status = await _jobStepDataRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 停止工步（工步状态由Running->Complete或者工步状态由Complete->SUPERSEDED,或者SUPERSEDED->Complete）
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task StopJobStepAsync(StoppingJobStepInput input)
        {
            //当前job必须运行在指定的entity上
            var jobExec = await _jobExecRepository.FindAsync(x => x.Ent_Id == input.Ent_Id && x.Cur_Wo_Id == input.Wo_Id && x.Cur_Oper_Id == input.Oper_Id);
            if (jobExec == null)
            {
                throw new CustomException(Messages.JobNotRunningOnEntity);
            }
            //当前工步必须存在
            var step = await _jobStepRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Step_Grp_Id == input.Step_Grp_Id && x.Step_No == input.Step_No);
            if (step == null)
            {
                throw new CustomException(Messages.JobStepNotFound);
            }
            ///工步状态可以修改成Complete/BYPassed/SUPERSEDED
            if (input.State_Cd != 4 && input.State_Cd != 8 && input.State_Cd != 9)
            {
                throw new CustomException(Messages.JobStepCanOnlySetToCompleteBypassedSuperseded);
            }
            var stepData = await _jobStepDataRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id && x.Step_Grp_Id == input.Step_Grp_Id && x.Step_No == input.Step_No).OrderByDescending(x => x.Row_Id).FirstOrDefaultAsync();
            if (stepData == null)
            {
                throw new CustomException(Messages.JobStepNotRunning);
            }

            //工步状态为Ruuning\Complete\BYPassed\SUPERSEDED时代表job正在运行，可以停止job
            if (stepData.State_Cd != 3 && stepData.State_Cd != 4 && stepData.State_Cd != 8 && stepData.State_Cd != 9)
            {
                throw new CustomException(Messages.JobStepNotRunning);
            }
            stepData.State_Cd = input.State_Cd;
            stepData.Act_Finish_Time_Local = DateTime.Now;
            stepData.Act_Finish_Time_Utc = DateTime.UtcNow;
            stepData.Last_Edit_At = DateTime.Now;

            //将JobExec当前工步支撑输入的工步
            jobExec.Cur_Step_No = null;
            jobExec.Cur_Step_Grp_Id = null;
            jobExec.Cur_Step_Start = null;
            jobExec.Last_Edit_At = DateTime.Now;

            var status = await _jobStepDataRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 查询存在job的实体列表
        /// </summary>
        /// <returns></returns>
        public async Task<ItemsResult<IdNameResult>> GetEntitiesWithJobsAsync()
        {
            var queryable = from job in _jobRepository.Query()
                            join ent in _productionUnitRepository.Query()
                            on job.Init_Sched_Ent_Id equals ent.Id
                            orderby job.Row_Id
                            select new IdNameResult
                            {
                                Id = ent.Id,
                                Name = ent.ProductionUnitName
                            };
            var entities = await queryable.Distinct().ToListAsync();
            return new ItemsResult<IdNameResult>() { Items = entities };
        }

        /// <summary>
        /// 作业-准备生产
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task ReadyJobAsync(ReadyingJobInput input)
        {
            //判断作业是否存在
            var job = await _jobRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.JobNotFound);
            }
            //判断job_exec是否存在记录
            var jobExec = await _jobExecRepository.FindAsync(x => x.Ent_Id == job.Init_Sched_Ent_Id);
            if (jobExec == null)
            {
                throw new CustomException(Messages.JobExecNotFound);
            }
            //仅仅New、Complete、Suspended、Onhold 状态可以修改成Ready状态
            if (job.State_Cd != 1 && job.State_Cd != 4 && job.State_Cd != 5 && job.State_Cd != 6)
            {
                throw new CustomException(Messages.JobReadyChangeStates);
            }
            //将作业的状态修改成Ready
            job.State_Cd = 2;
            job.Edit_Time = DateTime.Now;
            var status = await _jobRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 作业-运行所选作业
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task RunJobAsync(RunningJobInput input)
        {
            //TODO: 查询job表的conncurent_link，conncurent_link是批处理标志，如果多个作业的conncurent_link同时为1，运行在相同的实体中，而且状态相同，则代表这些作业时批处理作业，运行其中一个作业，会同时运行批处理中的其他作业。
           
            //判断生产单元是否存在
            var ent = await _productionUnitRepository.FindAsync(x => x.Id == input.Ent_Id);
            if (ent == null)
            {
                throw new CustomException(Messages.EntityNotFound);
            }
            //查询工单下的所有作业
            var jobs = await _jobRepository.Query().Where(x => x.Wo_Id == input.Wo_Id).ToListAsync();
            var job = new Job();
            if (jobs.Count > 0)
            {
                //判断需要运行的作业是否存在
                job = jobs.FirstOrDefault(x => x.Oper_Id == input.Oper_Id);
                if (job == null)
                {
                    throw new CustomException(Messages.JobNotFound);
                }
                //一个工单下只能有一个作业正在运行
                var leftjob = jobs.Where(x => x.Oper_Id != input.Oper_Id).ToList();
                if (leftjob.Count > 0)
                {
                    if (leftjob.Any(x => x.State_Cd == 3))
                    {
                        throw new CustomException(Messages.JobOfWoAlreadyRunning);
                    }
                }
            }
            else
            {
                throw new CustomException(Messages.WoHasNoJob);
            }
            //判断作业的调度地是否等于输入的生产单元
            if (job.Init_Sched_Ent_Id != input.Ent_Id)
            {
                throw new CustomException(Messages.EntityMustBeJobInitSchedEnt);
            }
            //判断工单是否存在
            var wo = await _woRepository.FindAsync(x => x.Wo_Id == input.Wo_Id);
            if (wo == null)
            {
                throw new CustomException(Messages.WoNotFound);
            }
            //工单状态如果是Closed（关闭），不能运行作业
            if (wo.State_Cd == 3)
            {
                throw new CustomException(Messages.CloseWoCanNotStartJob);
            }
            //判断job_exec表中是否存在记录
            var jobExec = await _jobExecRepository.FindAsync(x => x.Ent_Id == input.Ent_Id);
            if (jobExec == null)
            {
                throw new CustomException(Messages.JobExecNotFound);
            }
            //判断job_exec记录的Cur_Wo_Id是否为空，如果不为空，则代表当前实体已经有作业正在运行，则不能运行其他作业，因为一个实体只能运行一个作业
            if (jobExec.Cur_Wo_Id != null)
            {
                throw new CustomException(Messages.JobRunOnEntity);
            }
            //当作业状态为New、Ready、Complete、Suspended、Onhold时可以修改成Running状态
            if (job.State_Cd != 1 && job.State_Cd != 2 && job.State_Cd != 4 && job.State_Cd != 5 && job.State_Cd != 6)
            {
                throw new CustomException(Messages.JobRunningChangeStates);
            }

            //TODO: check入参check_privs的值是否为1，如果为1，则调用sp_S_Grp_Priv_Link_GetPriv获取当前用户运行job的权限，如果有权限，则开始运行作业

            //将jobExec表记录的当前工单编号、当前操作编号置成运行的作业的工单编号和操作编号
            jobExec.Cur_Wo_Id = input.Wo_Id;
            jobExec.Cur_Oper_Id=input.Oper_Id;
            jobExec.Cur_Step_No = null;
            jobExec.Cur_Step_Grp_Id = null;
            jobExec.Cur_Step_Start = null;
            jobExec.Last_Edit_At = DateTime.Now;

            //将作业状态修改成Running
            job.State_Cd = 3;
            job.Run_Ent_Id = input.Ent_Id;
            job.Act_Start_Time_Local = DateTime.Now;
            job.Act_Start_Time_Utc = DateTime.UtcNow;

            //将工单状态修改成Started
            wo.State_Cd = 1;
            wo.Last_Edit_At = DateTime.Now;

            var status = await _jobRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 作业-暂停
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task PauseJobAsync(PausingJobInput input)
        {
            //TODO: 查询job表的conncurent_link，conncurent_link是批处理标志，如果多个作业的conncurent_link同时为1，运行在相同的实体中，而且状态相同，则代表这些作业时批处理作业，暂停其中一个作业，会同时暂停批处理中的其他作业。

            //状态可以修改成New、Ready、Complete、Suspended、Onhold(Operator暂停只允许改成New、Suspended、Onhold这三种状态)
            if (input.Paused_Job_State != 1 && input.Paused_Job_State != 2 && input.Paused_Job_State != 4 && input.Paused_Job_State != 5 && input.Paused_Job_State != 6)
            {
                input.Paused_Job_State = 5;
            }
            //判断作业是否存在
            var job = await _jobRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.JobNotFound);
            }
            //当作业的当前状态是Running时，需要判断，当前job是否运行在当前实体上，如果不是，则报错
            if (job.State_Cd == 3)
            {
                var jobExec = await _jobExecRepository.FindAsync(x => x.Ent_Id == input.Ent_Id && x.Cur_Wo_Id == input.Wo_Id && x.Cur_Oper_Id == input.Oper_Id);
                if (jobExec == null)
                {
                    throw new CustomException(Messages.JobNotRunningOnEntity);
                }
                //将jobExec表记录的当前工单编号、当前操作编号、当前工步组编号、当前工步编号、当前工步开始时间全部置成空
                jobExec.Cur_Wo_Id = null;
                jobExec.Cur_Oper_Id = null;
                jobExec.Cur_Step_Grp_Id = null;
                jobExec.Cur_Step_No = null;
                jobExec.Cur_Step_Start = null;
                jobExec.Last_Edit_At = DateTime.Now;
            }

            //TODO: 当job当前的状态是onhold时，获取用户是否有权限pause一个onhold的job

            // 将当前作业状态修改成暂停状态
            job.State_Cd = input.Paused_Job_State;
            job.Act_Finish_Time_Utc = DateTime.UtcNow;
            job.Act_Finish_Time_Local = DateTime.Now;
            job.Edit_Time = DateTime.Now;

            var status = await _jobRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 作业-完成
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task EndJobAsync(EndingJobInput input)
        {
            //TODO: 查询job表的conncurent_link，conncurent_link是批处理标志，如果多个作业的conncurent_link同时为1，运行在相同的实体中，而且状态相同，则代表这些作业时批处理作业，完成其中一个作业，会同时完成批处理中的其他作业。

            //判断工单是否存在
            var wo = await _woRepository.FindAsync(x => x.Wo_Id == input.Wo_Id);
            if (wo == null)
            {
                throw new CustomException(Messages.WoNotFound);
            }
            //判断作业是否存在
            var job = await _jobRepository.FindAsync(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id);
            if (job == null)
            {
                throw new CustomException(Messages.JobNotFound);
            }
            //当作业当前状态为Ruuning和Suspended时，作业状态运行修改成完成状态
            if (job.State_Cd != 3 && job.State_Cd != 5)
            {
                throw new CustomException(Messages.JobCompleteChangeStates);
            }
            //当作业状态是Running时，需要判断作业是否运行在当前实体上
            var jobExec=new JobExec();
            if (job.State_Cd == 3)
            {
                jobExec = await _jobExecRepository.FindAsync(x => x.Ent_Id == input.Ent_Id && x.Cur_Wo_Id == input.Wo_Id && x.Cur_Oper_Id == input.Oper_Id);
                if (jobExec == null)
                {
                    throw new CustomException(Messages.JobNotRunningOnEntity);
                }
            }

            //TODO: 验证用户是否有权限完成作业
            //TODO: 验证用户执行完成作业操作的Client类型,如果Client类型是36（Supervisor），从job表中获取当前job的状态，如果job的状态是RUNNING, COMPLETE, CANCELLED, BYPASSED，则不允许完成作业
            //TODO: 验证作业的check_qtys是否为1，check_qtys是判断完成作业时，产量是否需要大于所需产量的标志，check_qtys是从job_exec表中的must_prod_reqd_qty判断，如果must_prod_reqd_qty为1，则check_qtys为1，如果must_prod_reqd_qty为0，则check_qtys为0，jobexec表的must_prod_reqd_qty来源于实体表（ent）的can_run_jobs字段（标志实体是否可以运行作业），如果can_run_jobs=1，则job_exec表的must_prod_reqd_qty=1

            if (job.Qty_Prod < job.Qty_Reqd)
            {
                throw new CustomException(Messages.JobQtyProdLessThenQtyProd);
            }

            //TODO: 获取产量阈值，并验证良品量+次品量是否介于开工量加减开工量*阈值之间，阈值设置在system_attr表中，id=104代表下限，id=103代表上限
            //验证作业的良品量不能小于作业的所需产量

            //TODO: 验证作业的check_steps是否为1,check_steps是判断完成作业时，工步是否需要全部完成的标志，check_steps是从job_exec表中的must_complete_steps判断，如果must_complete_steps为1，则check_steps为1，如果must_complete_steps为0，则check_steps为0，jobexec表的must_prod_reqd_qty来源于实体表（ent）的can_run_jobs字段（标志实体是否可以运行作业），如果can_run_jobs=1，则job_exec表的must_complete_steps=1
            //验证Job_Step是否全部完成
            var steps = await _jobStepRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id).ToListAsync() ;
            var stepDatas = await _jobStepDataRepository.Query().Where(x => x.Wo_Id == input.Wo_Id && x.Oper_Id == input.Oper_Id).ToListAsync();
            foreach (var item in steps)
            {
                var currentStepData = stepDatas.Where(x => x.Step_Grp_Id == item.Step_Grp_Id && x.Step_No == item.Step_No).ToList();
                if (currentStepData.Count <= 0)
                {
                    throw new CustomException(Messages.ExistJobStepNotComplete);
                }
                if (currentStepData.Any(x => x.State_Cd != 4 && x.State_Cd != 8 && x.State_Cd != 9))
                {
                    throw new CustomException(Messages.ExistJobStepNotComplete);
                }
            }

            //TODO: 调用sp_S_Check_Max_Min_Qty_Allowed以验证产量是否介于最大最小消耗量之间，还需要调查
            //TODO: 验证 Client的类型是否是37(Operator)或39(FC)时，当作业状态时Ruuning是，需要验证当前作业是否运行在实体上

            //当作业状态是Running时，更新JobExec，将jobExec表的当前工单编号、当前操作编号、当前工步组编号、当前工步编号、当前工步开始时间全部置成空
            if (job.State_Cd == 3)
            {
                jobExec.Cur_Wo_Id = null;
                jobExec.Cur_Oper_Id = null;
                jobExec.Cur_Step_Grp_Id = null;
                jobExec.Cur_Step_No = null;
                jobExec.Cur_Step_Start = null;
                jobExec.Last_Edit_At = DateTime.Now;
            }

            //将当前作业状态修改成完成状态
            job.State_Cd = 4;
            job.Act_Finish_Time_Utc = DateTime.UtcNow;
            job.Act_Finish_Time_Local = DateTime.Now;
            job.Edit_Time = DateTime.Now;

            //判断工单下的所有job，如果所有状态都是完成，则将工单状态置成完成
            var jobs = await _jobRepository.Query().Where(x => x.Wo_Id == input.Wo_Id).ToListAsync();
            var leftjob = jobs.Where(x => x.Oper_Id != input.Oper_Id).ToList();
            if (leftjob.Count > 0)
            {
                if (leftjob.All(x => x.State_Cd == 4))
                {
                    wo.State_Cd = 2;//完成
                    wo.Last_Edit_At = DateTime.Now;
                }
            }
            else
            {
                wo.State_Cd = 2;//完成
                wo.Last_Edit_At = DateTime.Now;
            }
            var status = await _jobRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }
    }
}
